use axum::body::Body;
use axum::http::Request;
use regex::Regex;
use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering;
use std::sync::{Arc, RwLock};
use std::{
    fmt,
    task::{Context, Poll},
};
use tklog::warn;

/// a demo https://tower-rs.github.io/tower/tower_layer/trait.Layer.html
use tower_layer::Layer;
use tower_service::Service;

#[derive(Debug, Clone)]
pub struct UrlLayer {
    articls_click_vue: Arc<RwLock<Vec<AtomicU16>>>,
}

impl UrlLayer {
    pub fn new(articls_click_vue: Arc<RwLock<Vec<AtomicU16>>>) -> Self {
        UrlLayer { articls_click_vue }
    }
}

impl<S> Layer<S> for UrlLayer {
    type Service = UrlService<S>;

    fn layer(&self, service: S) -> Self::Service {
        UrlService {
            service,
            articls_click_vue: Arc::clone(&self.articls_click_vue),
        }
    }
}

// This service implements the Log behavior
#[derive(Debug, Clone)]
pub struct UrlService<S> {
    service: S,
    articls_click_vue: Arc<RwLock<Vec<AtomicU16>>>,
}

impl<S> Service<Request<Body>> for UrlService<S>
where
    S: Service<Request<Body>>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, request: Request<Body>) -> Self::Future {
        let uri = request.uri();
        println!("the path is : {}", uri);
        let re = Regex::new(r"/article/(?P<id>\d+)").unwrap();

        if let Some(captures) = re.captures(uri.path()) {
            if let Some(id_match) = captures.name("id") {
                println!("Extracted ID: {}", id_match.as_str());
                let index: usize = match id_match.as_str().parse() {
                    Ok(num) => num,
                    Err(_) => {
                        warn!("Failed to parse article ID.");
                        return self.service.call(request);
                    }
                };
                let mut vec = self.articls_click_vue.write().unwrap();
                // 确保 vec 的长度至少为 index + 1
                while vec.len() <= index {
                    vec.push(AtomicU16::new(0));
                }
                // 更新点击次数
                vec[index].fetch_add(1, Ordering::SeqCst);
                println!(
                    "Article {} has been clicked {} times.",
                    index,
                    vec[index].load(Ordering::SeqCst)
                );
            }
        } else {
            println!("No match found.");
        }
        self.service.call(request)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use regex::Regex;

    #[test]
    pub fn test_regex() {
        let re = Regex::new(r"Hello (?<name>\w+)!").unwrap();
        let Some(caps) = re.captures("Hello Murphy!") else {
            println!("no match!");
            return;
        };
        println!("The name is: {}", &caps["name"]);
    }
}
